package scene

import (
	"cgs-server/helper"
	"cgs-server/pkg/file"
	"cgs-server/server"
	"cgs-server/server/assets/cgs"
	"cgs-server/server/assets/mesh"
	"cgs-server/server/base"
	"cgs-server/server/model"
	"cgs-server/server/system/user"
	"errors"
	"fmt"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo/options"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"
)

var imageNames = [6]string{"px", "nx", "py", "ny", "pz", "nz"}

/**
场景 Service
*/
type SceneService interface {
	/**
	参数: 场景名称和数据
	返回 id 和 error
	*/
	Add(model *AddModel) *base.AddResult
	// id：cgsProjectId
	Export(id primitive.ObjectID) ([]*model.ZipFileData, error)
	Import(data []*model.ZipFileData, serverHost string, user *model.User) (string, error)

	base.Service

	// 根据工程 id 获取
	GetSceneDataByProjectId(projectId string, version int, user *model.User) ([]bson.M, error)
}

func NewSceneService() SceneService {
	return &sceneService{
		sceneRepo: NewSceneRepository(),
	}
}

type sceneService struct {
	sceneRepo SceneRepository
	base.ServiceImpl
}

func (s *sceneService) Add(model *AddModel) *base.AddResult {
	// sceneID
	model.IDObjId = primitive.NewObjectID()
	model.ID = model.IDObjId
	model.CollectionName = "Scene" + helper.TimeToString(time.Now(), "yyyyMMddHHmmss")
	model.Version = new(int32)

	if model.Name == "" {
		return &base.AddResult{Error: errors.New("name is not allowed to be empty")}
	}
	if strings.HasPrefix(model.Name, "_") {
		return &base.AddResult{Error: errors.New("name is not allowed to start with _")}
	}

	err := s.sceneRepo.CreateDB(server.SceneCollectionName)
	if err != nil {
		return &base.AddResult{Error: err}
	}

	// 添加一条场景信息
	_, err = s.sceneRepo.Add(model)
	if err != nil {
		return &base.AddResult{Error: err}
	}

	// save new scene data

	_, err = s.sceneRepo.AddSceneData(model.CollectionName, model.Data)
	if err != nil {
		return &base.AddResult{Error: err}
	}

	return &base.AddResult{Id: model.ID.Hex(), Msg: "Add successfully.", Error: nil}
}

func (s *sceneService) Export(id primitive.ObjectID) ([]*model.ZipFileData, error) {
	//压缩
	var zipData []*model.ZipFileData
	//获取projectID
	projectId, _ := s.sceneRepo.FindByProjectId(id)

	//通过project中的js文件获得sceneID
	r := regexp.MustCompile(`.*sceneID\s*:\s*['|"](\w*)['|"]`)
	sceneID := r.FindStringSubmatch(projectId.Data)

	if len(sceneID) < 2 {
		return zipData, errors.New("not find SceneID")
	}
	sceneId, _ := primitive.ObjectIDFromHex(sceneID[1])

	//sceneJson数据
	scene, _ := s.sceneRepo.FindBySceneId(sceneId)
	res, err := helper.ToJSON(scene)
	if err != nil {
		fmt.Println("read fail", err)
	}

	//获得scene表格数据
	result, _ := s.sceneRepo.FindAllFromScene(scene.CollectionName)
	resScene, err := helper.ToJSON(result)
	if err != nil {
		fmt.Println("read fail", err)
	}

	//将数据写入文件
	if len(resScene) > 0 && len(res) > 0 {
		zipData = append(zipData, &model.ZipFileData{
			Name: server.ExportEditorFile + "sceneTable.json",
			Body: resScene,
		})
		zipData = append(zipData, &model.ZipFileData{
			Name: server.ExportEditorFile + "scene.json",
			Body: res,
		})
	} else {
		return zipData, nil
	}

	//遍历表单导出模型文件
	for _, value := range result {
		newValue, ok := value.(bson.D)
		if !ok {
			continue
		}
		valueMap := newValue.Map()
		if valueMap["userData"] == nil {
			continue
		}

		u := valueMap["userData"].(bson.D).Map()

		// 背景图
		imageZipData := exportImage(valueMap)
		zipData = append(zipData, imageZipData...)

		//获得文件路径
		if u["Server"] == true && u["Url"] != nil {

			flag := false
			for _, zipValue := range zipData {
				if strings.Contains(zipValue.Name, u["ID"].(string)) {
					flag = true
				}
			}

			if flag {
				continue
			}

			// 3DTiles 模型不需要导出
			if u["Type"].(string) == "3DTiles" {
				continue
			}

			//LastIndex := strings.LastIndex(u["Url"].(string), "/")
			meshService := mesh.NewMeshService()
			meshData, err := meshService.FindMeshById(u["ID"].(string))
			if err != nil {
				return nil, err
			}
			if meshData == nil {
				return nil, fmt.Errorf("Mesh '%s' not found, id: '%s'", u["Name"], u["ID"])
			}
			physicalPath := server.MapPath(meshData.SavePath)
			tempPath := filepath.Join(physicalPath, "temp", meshData.FileName)

			jsonFile := []byte{}
			if _, err := os.Stat(tempPath); os.IsNotExist(err) {
				// 如果压缩包不存在就压缩模型文件
				filePath := physicalPath + string(filepath.Separator) // 模型文件夹路径
				file, err := base.ZipFileToBuffer(filePath)
				if err != nil {
					return nil, err
				}
				jsonFile = file.Bytes()
				//fmt.Println("压缩模型文件")
			} else {
				// 如果压缩包存在，就直接读取zip文件
				//fmt.Println("tempPath: ", tempPath)
				jsonFile, err = ioutil.ReadFile(tempPath)
				if err != nil {
					return nil, err
				}
				//fmt.Println("直接读取模型zip文件")
			}

			if len(jsonFile) > 0 {
				zipData = append(zipData, &model.ZipFileData{
					Name: server.ExportEditorFile + "model/" + u["ID"].(string) + "/" + meshData.FileName,
					Body: jsonFile,
				})
			}
		}
	}

	return zipData, nil
}

// exportImage 导出背景图片
func exportImage(data bson.M) (imageZipData []*model.ZipFileData) {
	if data["background"] == nil {
		return
	}
	bg, ok := data["background"].(bson.D)
	if !ok {
		return
	}
	background := bg.Map()
	if background["image"] == nil {
		return
	}

	if br, ok := background["image"].(bson.A); ok {
		// 天空盒
		if len(br) != 6 {
			return
		}
		for i, bi := range br {
			b := bi.(bson.D).Map()
			imageSrc := b["src"].(string)
			imageName := filepath.Base(imageSrc)
			bodyBytes, err := helper.Request(imageSrc)
			if err != nil {
				continue
			}
			imagePath := ""
			if strings.HasPrefix(imageName, imageNames[i]) {
				imagePath = fmt.Sprintf("%sbackground/%s", server.ExportEditorFile, imageName)
			} else {
				imagePath = fmt.Sprintf("%sbackground/%s_%s", server.ExportEditorFile, imageNames[i], imageName)
			}
			imageZipData = append(imageZipData, &model.ZipFileData{
				Name: imagePath,
				Body: bodyBytes,
			})
		}
	}

	if br, ok := background["image"].(bson.D); ok {
		// 单个背景图
		b := br.Map()
		imageSrc := b["src"].(string)
		imageName := filepath.Base(imageSrc)
		bodyBytes, err := helper.Request(imageSrc)
		if err != nil {
			return
		}
		imageZipData = append(imageZipData, &model.ZipFileData{
			Name: fmt.Sprintf("%sbackground/%s", server.ExportEditorFile, imageName),
			Body: bodyBytes,
		})
	}

	return
}

func importImage(data *map[string]interface{}, zipData []*model.ZipFileData, serverHost string) {
	var err error
	d := *data
	if d["background"] == nil {
		return
	}

	background, ok := d["background"].(map[string]interface{})
	if !ok || background["image"] == nil {
		return
	}

	now := time.Now()
	savePath := fmt.Sprintf("/Upload/Texture/%v", helper.TimeToString(now, "yyyyMMddHHmmss"))
	physicalPath := server.MapPath(savePath)

	if br, ok := background["image"].([]interface{}); ok {
		// 天空盒
		if len(br) != 6 {
			return
		}
		for _, bi := range br {
			b := bi.(map[string]interface{})
			imageSrc := b["src"].(string)
			imageName := filepath.Base(imageSrc)

			// 两种方式获取图片数据
			var bodyBytes []byte
			for _, datum := range zipData {
				if strings.HasSuffix(datum.Name, imageName) {
					bodyBytes = datum.Body
					break
				}
			}

			if len(bodyBytes) == 0 {
				bodyBytes, err = helper.Request(imageSrc)
				if err != nil {
					continue
				}
			}

			err = file.WriteFileWithBytes(physicalPath, imageName, bodyBytes)
			if err != nil {
				continue
			}
			b["src"] = fmt.Sprintf("%s%s/%s", serverHost, savePath, imageName)
		}
	}

	if b, ok := background["image"].(map[string]interface{}); ok {
		// 单个背景图
		imageSrc := b["src"].(string)
		imageName := filepath.Base(imageSrc)

		// 两种方式获取图片数据
		var bodyBytes []byte
		for _, datum := range zipData {
			if strings.HasSuffix(datum.Name, imageName) {
				bodyBytes = datum.Body
				break
			}
		}

		if len(bodyBytes) == 0 {
			bodyBytes, err = helper.Request(imageSrc)
			if err != nil {
				return
			}
		}

		err = file.WriteFileWithBytes(physicalPath, imageName, bodyBytes)
		if err != nil {
			return
		}
		b["src"] = fmt.Sprintf("%s/%s/%s", serverHost, savePath, imageName)
	}

	return
}

func (s *sceneService) Import(data []*model.ZipFileData, serverHost string, user *model.User) (string, error) {
	now := time.Now()
	sceneCollectName := "Scene" + helper.TimeToString(now, "yyyyMMddHHmmss")
	sceneID := primitive.NewObjectID()
	for _, value := range data {
		if strings.HasSuffix(value.Name, "scene.json") {
			var addModel *Model

			if err := helper.FromJSON(value.Body, &addModel); err != nil {
				return "", err
			}

			addModel.ID = sceneID
			//addModel.Name += "_copy"
			addModel.CollectionName = sceneCollectName
			addModel.Version = new(int32)
			addModel.UserId = user.UserId.Hex()
			addModel.UpdateTime = now
			addModel.CreateTime = now
			addModel.TenantId = user.TenantId

			// 存数据库，记得打开
			err := s.sceneRepo.Insert(&model.DataUserModel{
				Data: addModel,
				User: user,
			})
			if err != nil {
				return "", err
			}
		} else if strings.HasSuffix(value.Name, "sceneTable.json") {
			var sceneTable []interface{}
			meshs := make(map[string]bson.M)

			modelsPathMap := make(map[string]string)

			if err := helper.FromJSON(value.Body, &sceneTable); err != nil {
				return "", err
			}
			for i := range sceneTable {
				valueMap, ok := sceneTable[i].(map[string]interface{})
				if !ok || valueMap["userData"] == nil {
					continue
				}

				importImage(&valueMap, data, serverHost)

				// metadata 为 OptionsSerializer 的 server 字段的修改
				if valueMap["metadata"] != nil && valueMap["server"] != nil {
					if _, ok := valueMap["server"].(string); ok {
						valueMap["server"] = serverHost
					}
				}

				u := valueMap["userData"].(map[string]interface{})

				//获得文件路径
				if u["Server"] == true && u["Url"] != nil && u["Name"] != nil && u["ID"] != nil {

					for _, modelData := range data {
						if !strings.Contains(modelData.Name, u["ID"].(string)) {
							continue
						}
						if m, ok := meshs[u["ID"].(string)]; ok {
							// 已经导入过了，只需要修改下 ID，URL
							u["Url"] = m["Url"]
							u["ID"] = m["_id"].(primitive.ObjectID).Hex()
							u["id"] = u["ID"]
							break
						}

						zipFile := mesh.ZipFile{
							Filename: u["Name"].(string) + ".zip",
							Size:     int64(len(modelData.Body)),
							FileType: "application/x-zip-compressed",
							Content:  modelData.Body,
						}
						//meshs = append(meshs, u["ID"].(string))
						meshService := mesh.NewMeshService()
						doc, err := meshService.UploadMesh(&zipFile, user)
						if err != nil {
							return "", err
						}
						meshs[u["ID"].(string)] = doc

						// 记录模型的路径中的唯一地址
						oldUrlSplit := strings.Split(u["Url"].(string), "/")
						newUrlSplit := strings.Split(doc["Url"].(string), "/")
						modelsPathMap[oldUrlSplit[len(oldUrlSplit)-2]] = newUrlSplit[len(newUrlSplit)-2]

						u["Url"] = doc["Url"]
						u["ID"] = doc["_id"].(primitive.ObjectID).Hex()
						u["id"] = u["ID"]
					}
				}
			}

			sceneBytes, err := helper.ToJSON(sceneTable)
			if err != nil {
				return "", err
			}

			var sceneString string
			for k, v := range modelsPathMap {
				sceneString = strings.ReplaceAll(string(sceneBytes), k, v)
			}

			if sceneString != "" {
				err = helper.FromJSON([]byte(sceneString), &sceneTable)
				if err != nil {
					return "", err
				}
			}

			// 存数据库
			err = s.sceneRepo.InsertSceneTable(sceneCollectName, &sceneTable)
			if err != nil {
				return "", err
			}
		}
	}

	return sceneID.Hex(), nil
}

func (s *sceneService) Save(mdl *model.DataUserModel) error {
	if scene, ok := mdl.Data.(*Model); ok {
		sceneRes := &Model{}
		now := time.Now()
		if scene.ID.IsZero() {
			// 新增场景
			scene.CollectionName = "Scene" + helper.TimeToString(now, "yyyyMMddHHmmss")
			scene.Version = new(int32)
			pinyin := helper.ConvertToPinYin(scene.Name)
			scene.TotalPinYin = pinyin.TotalPinYin
			scene.FirstPinYin = pinyin.FirstPinYin
			scene.CreateTime = now
			scene.UpdateTime = now
			// 新增
			err := s.sceneRepo.Insert(mdl)
			if err != nil {
				return err
			}
		} else {
			// 查找场景
			result, err := s.sceneRepo.FindOne(mdl)
			if err != nil {
				return err
			}
			if result == nil {
				return errors.New("scene is not found")
			}
			sceneRes = result.(*Model)
			scene.CollectionName = sceneRes.CollectionName
			if sceneRes.Version != nil {
				scene.Version = sceneRes.Version
			} else {
				scene.Version = new(int32)
			}
			*scene.Version++

			// 更新
			err = s.sceneRepo.Update(mdl)
			if err != nil {
				return err
			}
			// 复制当前场景数据到历史库
			old, err := s.sceneRepo.FindSceneData(scene.CollectionName, -1)
			if err != nil {
				return err
			}
			if len(old) > 0 {
				// remove _id; otherwise deplicated
				for i := 0; i < len(old); i++ {
					delete(old[i], "_id")
				}
				var oldData []interface{}
				for _, i := range old {
					oldData = append(oldData, i)
				}
				err := s.sceneRepo.InsertSceneTable(scene.CollectionName+server.HistorySuffix, &oldData)
				if err != nil {
					return err
				}
			}
		}
		// save new scene data
		var list []interface{}
		if len(scene.DataArr) > 0 {
			list = scene.DataArr
		} else {
			err := bson.UnmarshalExtJSON([]byte(scene.Data), false, &list)
			if err != nil {
				return err
			}
		}
		err := s.sceneRepo.DeleteSceneData(scene.CollectionName)
		if err != nil {
			return err
		}
		err = s.sceneRepo.InsertSceneTable(scene.CollectionName, &list)
		return err
		// 场景数据的数据库名称
	} else {
		return errors.New("Scene type error")
	}
}

func (s *sceneService) Update(mdl *model.DataUserModel) error {
	return s.sceneRepo.Update(mdl)
}

func (s *sceneService) List(mdl *model.DataUserModel) (interface{}, error) {
	if _, ok := mdl.Data.(*Model); ok {
		//todo 查找分类,获取分类名称

		filter := bson.M{}
		if mdl.User.RoleName == "AppAdministrator" || mdl.User.RoleName == "Administrator" {
			// 可以获取所有场景
		} else {
			if len(mdl.User.AppAuths) == 0 {
				// 没有任何应用，即表示不拥有任何场景
				return nil, nil
			}
			// 查询拥有的cgsProject 列表
			//fmt.Println(filter)
			//fmt.Println(scene)
			cgsService := cgs.NewCgsService()
			duModel := &model.DataUserModel{
				Data: &cgs.CgsProj{},
				User: mdl.User,
			}
			projectList, err := cgsService.List(duModel)
			if err != nil {
				return nil, err
			}
			if projectList == nil {
				return nil, nil
			}
			projects := projectList.([]*cgs.CgsProj)
			// 获取场景ids, 通过权限过滤
			userService := user.NewUserService()
			userResult, err := userService.GetByUserIdOrUserName(&model.DataUserModel{
				Data: mdl.User,
				User: mdl.User,
			})
			if err != nil {
				return nil, err
			}
			if userResult == nil {
				return nil, errors.New("Permission denied.")
			}
			//通过project中的js文件获得sceneID
			sceneIds := bson.A{}
			for i := range projects {
				sceneID, _ := projects[i].GetSceneId()
				sceneBsonId, err := primitive.ObjectIDFromHex(sceneID)
				if err != nil {
					continue
				}
				if userResult.HasEditSceneAuth(sceneID) {
					sceneIds = append(sceneIds, sceneBsonId)
				}
			}
			filter = bson.M{
				"ID": bson.M{
					"$in": sceneIds,
				},
			}
		}
		mdl.Filter = &filter
		mdl.Options = &options.FindOptions{
			Sort: bson.M{
				"UpdateTime": -1,
				"updateTime": -1,
			},
		}
		results, err := s.sceneRepo.Find(mdl)
		if err != nil {
			return nil, err
		}
		return results, nil
	} else {
		return nil, model.NewTypeError("scene")
	}
}

func (s *sceneService) GetSceneDataByProjectId(projectId string, version int, user *model.User) ([]bson.M, error) {
	bsonId, err := primitive.ObjectIDFromHex(projectId)
	if err != nil {
		return nil, err
	}
	cgsService := cgs.NewCgsService()
	cgsProj, err := cgsService.GetOne(&model.DataUserModel{
		Data: &cgs.CgsProj{
			ID: bsonId,
		},
		User: user,
	})
	if err != nil {
		return nil, err
	}
	if cgsProj == nil {
		return nil, errors.New("Cgs project is not found.")
	}
	sceneId, err := cgsProj.(*cgs.CgsProj).GetSceneId()
	if err != nil {
		return nil, err
	}
	sceneBsonId, err := primitive.ObjectIDFromHex(sceneId)
	if err != nil {
		return nil, err
	}

	sceneRes, err := s.sceneRepo.FindOne(&model.DataUserModel{
		Data: &Model{
			ID: sceneBsonId,
		},
		User: user,
	})
	if err != nil {
		return nil, err
	}
	if sceneRes == nil {
		return nil, errors.New("Scene is not found.")
	}
	collectionName := sceneRes.(*Model).CollectionName

	data, err := s.sceneRepo.FindSceneData(collectionName, version)
	if err != nil {
		return nil, err
	}
	return data, nil
}

func (s *sceneService) GetOne(mdl *model.DataUserModel) (interface{}, error) {
	return s.sceneRepo.FindOne(mdl)
}

func (s *sceneService) Remove(mdl *model.DataUserModel) error {
	if _, ok := mdl.Data.(*Model); ok {
		// 查询scene
		one, err := s.sceneRepo.FindOne(mdl)
		if err != nil {
			return err
		}
		if one == nil {
			return model.NotFoundError("scene")
		}
		sceneResult := one.(*Model)
		err = s.sceneRepo.Delete(mdl)
		if err != nil {
			return err
		}
		// 删除场景数据表
		err = s.sceneRepo.DropSceneDataCollection(sceneResult.CollectionName)
		return err
	} else {
		return model.NewTypeError("scene")
	}
}

func (s *sceneService) RemoveLogic(mdl *model.DataUserModel) error {
	if _, ok := mdl.Data.(*Model); ok {
		// 查询scene
		one, err := s.sceneRepo.FindOne(mdl)
		if err != nil {
			return err
		}
		if one == nil {
			return model.NotFoundError("scene")
		}
		//sceneResult := one.(*Model)
		err = s.sceneRepo.DeleteLogic(mdl)
		if err != nil {
			return err
		}
		// 逻辑删除，不用删除场景数据表
		//err = s.sceneRepo.DropSceneDataCollection(sceneResult.CollectionName)
		return err
	} else {
		return model.NewTypeError("scene")
	}
}
